package Recomendadores.Semantico;

import java.util.ArrayList;
import java.util.Iterator;

import UserProfile.Configuration;
import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

public class Filtrado {

	/**Filtra de todas las posibles secuencias de propiedades, aquellas relevantes para el usuario, 
	 * partiendo de la pelicula a la que se desea inferir el DOI
	 *  
	 * @param user perfil-ontologia del usuario, de donde se extraeran las secuencias de propiedades
	 * @param movie instancia de la pelicula a la que se desea inferir el DOI
	 * @return las secuencias de propiedades relevantes para el usuario	 
	 */
	public static ArrayList<PropertySequence> filtradoDePropiedades(OntModel user, Individual movie) {
		if(user!=null && movie!=null){
			//obtengo las secuencias de propiedades relevantes
			ArrayList<PropertySequence> secuencias = getPropertySequences(movie,user);
			return secuencias;
		}else{
			System.out.println("Perfil de usuario o individual null");
			return null;
		}

	}


	/**Filtra de todas las posibles secuencias de propiedades, aquellas relevantes para el usuario, 
	 * partiendo de la pelicula a la que se desea inferir el DOI
	 *  
	 * @param user perfil-ontologia del usuario, de donde se extraeran las secuencias de propiedades
	 * @param movie instancia de la pelicula a la que se desea inferir el DOI
	 * @return las secuencias de propiedades relevantes para el usuario	 
	 */
	private static ArrayList<PropertySequence> getPropertySequences(Individual movie, OntModel user) {
		//creo la lista que contendra las secuencias relevantes para el usuario
		ArrayList<PropertySequence> finalSequences = new ArrayList<PropertySequence>();

		//creo una nueva secuencia de propiedades y defino a la pelicula como comienzo de la secuencia
		PropertySequence sequence = new PropertySequence();
		sequence.add(movie);

		//agrego la secuencia a la lista final
		finalSequences.add(sequence);		
		System.out.print(movie.getLocalName());
		/*a partir de esta pelicula inicial obtengo las instancias 
		 * relacionadas semanticamente y relevantes para el usuario*/
		finalSequences = continueProperySequences(finalSequences, user);
		System.out.println("acabo de sacar las secuencias");
		return finalSequences;		
	}

	/**Continua desarrollando las secuencias de propiedades relevantes para el usuario, 
	 * partiendo de la ultima instancia de la secuencia, este metodo opera de forma recursiva
	 *  
	 * @param user perfil-ontologia del usuario, de donde se extraeran las secuencias de propiedades
	 * @param in lista de secuencias de propiedades relevantes para el usuario
	 * @return las secuencias de propiedades relevantes para el usuario	 
	 */
	private static ArrayList<PropertySequence> continueProperySequences(ArrayList<PropertySequence> in, OntModel user) {
		Configuration conf = Configuration.getInstance();
		ObjectProperty hasyear = user.getObjectProperty(conf.getNS() + "hasYear"); //propiedades que necesito

		//creo la lista que contendra las secuencias de propiedades encontradas
		ArrayList<PropertySequence> finalSequence = new ArrayList<PropertySequence>();
		Iterator<PropertySequence> i = in.iterator();
		//recorro las secuencias
		while(i.hasNext()){
			boolean existenNuevasSecuencias=false; //booleano auxiliar para comprobar si fueron cradas nuevas secuencias
			PropertySequence sequence = i.next();
			ArrayList<Individual> arrayRes = sequence.getSequence(); //obtengo la secuencia de instancias
			Iterator<Individual> j = arrayRes.iterator();
			Individual individual = null;
			//obtengo la ultima instancia de la secuencia
			while(j.hasNext()){
				individual = j.next();
			}

			//se continua la busqueda de nuevas instancias hasta que el largo de la secuencia alcanza un humbral establecido
			int largo = sequence.length();
			if(individual!=null && largo<conf.getPATH()){
                            
				//obtengo la clase padre de la instancia, para obtener las propiedades de la clase				
				OntClass clase = individual.getOntClass();

				/*Diferencio entre generos y otras caracteristicas. 
				 *En el caso de los generos se obtienen las propiedades de la superclase ContentCS, 
				 *las demas caracteristicas solo se obtienen las propiedades de la clase (ej. ACTOR) utilizando el true
				 *Esto se debe la la forma en que esta estructurada la ontologia.
				 */
				OntClass superclass = clase.getSuperClass();
				ExtendedIterator<OntProperty> propiedades;
				if(superclass.getLocalName().equals("ContentCS")){
					propiedades = clase.listDeclaredProperties();
				}else{
					propiedades = clase.listDeclaredProperties(true);
				}

				//obtengo y recorro las propiedades de esa instancia
				while(propiedades.hasNext()){
					OntProperty prop = propiedades.next();
					if(prop.isObjectProperty()){ //si es propiedad objeto sigo, si no, no me interesa
						//elimino las relaciones con anio, son poco interesantes
						if(!prop.getLocalName().equals(hasyear.getLocalName())){

							ObjectProperty p = prop.asObjectProperty();
							StmtIterator k = individual.listProperties(p);
							//recorro las propiedades y obtengo las instancias relacionadas
							while(k.hasNext()){
								Statement stat =k.next();
                                                                //System.out.println(stat.asTriple().toString());
								if(stat!=null){
									Resource res = stat.getResource(); //obtengo la instancia asociada a la pelicula
									Individual instancia = user.getIndividual(res.getURI());
									//System.out.println(instancia.toString());
                                                                        //si la instancia no existe en la secuencia la agrego, en caso contrario no la agrego para evitar bucles e informacion duplicada
									if(!existeEnLaSecuencia(sequence,instancia)){
										//obtengo el DOI de la instancia para comprobar que posee informacion, de otra forma sera termino de secuencia
										int instdoi = getDOI(instancia, user);
										//if(instdoi!=0){
										/*si el DOI de la instancia relacionada semanticamente no supera el DOI=3, 
										 *no es una instancia relevante y no se toma en cuanta*/
										if(instdoi>=3){
											/*ademas compruebo que el DOI sea mayor o igual al de la instancia anterior en la secuencia, 
											 *de otra manera la informacion que aporta es irrelevante*/
											if(instdoi>=getDOI(individual, user)){
												//creo una nueva secuencia con base en la secuencia original
												PropertySequence nuevaSecuencia = new PropertySequence(sequence.getSequence());
												nuevaSecuencia.add(instancia); //agrego la nueva instancia encontrada
												finalSequence.add(nuevaSecuencia); //agrego la nueva secuencia en la lista final
												existenNuevasSecuencias = true; //levanto la bandera de que una nueva secuencia fue encontrada
											}
										}
									}
								}
							}
						}
					}
				}				
			}
			//cuando termino la busqueda de nuevas secuencias relevantes compruebo que efectivamente se hallan encontrado nuevas secuencias
			if(!existenNuevasSecuencias){
				//si no se encontraron nuevas secuencias, la secuencia actual es una secuencia final
				finalSequence.add(sequence);
			}			
		}

		//Cuando termina la busqueda a partir de las secuencias anteriores, se comprueba si la la lista de secuencias in 
		//es diferente a la lista de secuencias luego de la busqueda, si son diferentes es porque se encontaron nuevas secuancias
		if(!in.equals(finalSequence)){
			System.out.print("|");
			//si se encontraron nuevas secuencias se continua la busqueda
			finalSequence = continueProperySequences(finalSequence, user);
		}
		//de otra manera, termino la busqueda
		return finalSequence;
	}

	/**Metodo que comprueba si una instancia existe previamente en la secuencia de propiedades
	 *  
	 * @param instance instancia que se desea comprobar si preexsite en la secuencia
	 * @param sequence secuencia de propiedades relevantes para el usuario
	 * @return true en el caso de que exista previamente, false en caso contrario 
	 */
	private static boolean existeEnLaSecuencia(PropertySequence sequence, Resource instance) {
		ArrayList<Individual> arrayRes = sequence.getSequence();
		Iterator<Individual> i = arrayRes.iterator();
		//recorre la secuancia y comprueba que la instancia no exista previamente
		while(i.hasNext()){
			String uriSec = i.next().getURI();
			String uriInst = instance.getURI();
			if(uriSec.equals(uriInst)){
				return true;
			}
		}
		return false;
	}

	/**Metodo que obtiene el DOI de una instancia para un usuario
	 *  
	 * @param instance instancia a la que se desea obtener el DOI 
	 * @param user perfil-ontologia del usuario
	 * @return el valor del DOI 
	 */
	public static int getDOI(Individual instance, OntModel user){
		Configuration conf = Configuration.getInstance();		
		int doi = 0;
		//obtengo las propiedades que necesito
		Property hasDOI = user.getProperty(conf.getNS() + "hasDOI");
		RDFNode prop = instance.getPropertyValue(hasDOI); //obtengo la propiededad hasDOI 
		if (prop !=null){
			doi = prop.asLiteral().getInt();			
		}
		return doi;
	}
        public static ArrayList<PropertySequence> filtradoDePropiedades(OntModel user, Individual movie, String props) {
		if(user!=null && movie!=null){
			//obtengo las secuencias de propiedades relevantes
			ArrayList<PropertySequence> secuencias = getPropertySequences(movie,user,props);
			return secuencias;
		}else{
			System.out.println("Perfil de usuario o individual null");
			return null;
		}

	}
        
        private static ArrayList<PropertySequence> getPropertySequences(Individual movie, OntModel user, String props) {
		//creo la lista que contendra las secuencias relevantes para el usuario
		ArrayList<PropertySequence> finalSequences = new ArrayList<PropertySequence>();

		//creo una nueva secuencia de propiedades y defino a la pelicula como comienzo de la secuencia
		PropertySequence sequence = new PropertySequence();
		sequence.add(movie);

		//agrego la secuencia a la lista final
		finalSequences.add(sequence);		
		System.out.print(movie.getLocalName());
		/*a partir de esta pelicula inicial obtengo las instancias 
		 * relacionadas semanticamente y relevantes para el usuario*/
		finalSequences = continueProperySequences(finalSequences, user,props);
		System.out.println("acabo de sacar las secuencias");
		return finalSequences;		
	}
        
        private static ArrayList<PropertySequence> continueProperySequences(ArrayList<PropertySequence> in, OntModel user, String props) {
		Configuration conf = Configuration.getInstance();
		ObjectProperty hasyear = user.getObjectProperty(conf.getNS() + "hasYear"); //propiedades que necesito
                ObjectProperty hasactor = user.getObjectProperty(conf.getNS() + "hasActor"); //propiedades que necesito
                ObjectProperty hasdirector = user.getObjectProperty(conf.getNS() + "hasDirector"); //propiedades que necesito
                ObjectProperty haswriter = user.getObjectProperty(conf.getNS() + "hasWriter"); //propiedades que necesito
                boolean actor= props.contains("actor");
                boolean director=props.contains("director");
                boolean writer=props.contains("writer");
                
                
                
                
                
		//creo la lista que contendra las secuencias de propiedades encontradas
		ArrayList<PropertySequence> finalSequence = new ArrayList<PropertySequence>();
		Iterator<PropertySequence> i = in.iterator();
		//recorro las secuencias
		while(i.hasNext()){
			boolean existenNuevasSecuencias=false; //booleano auxiliar para comprobar si fueron cradas nuevas secuencias
			PropertySequence sequence = i.next();
			ArrayList<Individual> arrayRes = sequence.getSequence(); //obtengo la secuencia de instancias
			Iterator<Individual> j = arrayRes.iterator();
			Individual individual = null;
			//obtengo la ultima instancia de la secuencia
			while(j.hasNext()){
				individual = j.next();
			}

			//se continua la busqueda de nuevas instancias hasta que el largo de la secuencia alcanza un humbral establecido
			int largo = sequence.length();
			if(individual!=null && largo<conf.getPATH()){
                            
				//obtengo la clase padre de la instancia, para obtener las propiedades de la clase				
				OntClass clase = individual.getOntClass();

				/*Diferencio entre generos y otras caracteristicas. 
				 *En el caso de los generos se obtienen las propiedades de la superclase ContentCS, 
				 *las demas caracteristicas solo se obtienen las propiedades de la clase (ej. ACTOR) utilizando el true
				 *Esto se debe la la forma en que esta estructurada la ontologia.
				 */
				OntClass superclass = clase.getSuperClass();
				ExtendedIterator<OntProperty> propiedades;
				if(superclass.getLocalName().equals("ContentCS")){
					propiedades = clase.listDeclaredProperties();
				}else{
					propiedades = clase.listDeclaredProperties(true);
				}

				//obtengo y recorro las propiedades de esa instancia
				while(propiedades.hasNext()){
					OntProperty prop = propiedades.next();
					if(prop.isObjectProperty()){ //si es propiedad objeto sigo, si no, no me interesa
						//elimino las relaciones con anio, son poco interesantes
						if(!prop.getLocalName().equals(hasyear.getLocalName()) 
                                                   && !(!actor && prop.getLocalName().equals(hasactor.getLocalName()))
                                                   && !(!director && prop.getLocalName().equals(hasdirector.getLocalName()))
                                                   && !(!writer && prop.getLocalName().equals(haswriter.getLocalName()))
                                                        ){

							ObjectProperty p = prop.asObjectProperty();
							StmtIterator k = individual.listProperties(p);
							//recorro las propiedades y obtengo las instancias relacionadas
							while(k.hasNext()){
								Statement stat =k.next();
                                                                //System.out.println(stat.asTriple().toString());
								if(stat!=null){
									Resource res = stat.getResource(); //obtengo la instancia asociada a la pelicula
									Individual instancia = user.getIndividual(res.getURI());
									//System.out.println(instancia.toString());
                                                                        //si la instancia no existe en la secuencia la agrego, en caso contrario no la agrego para evitar bucles e informacion duplicada
									if(!existeEnLaSecuencia(sequence,instancia)){
										//obtengo el DOI de la instancia para comprobar que posee informacion, de otra forma sera termino de secuencia
										int instdoi = getDOI(instancia, user);
										//if(instdoi!=0){
										/*si el DOI de la instancia relacionada semanticamente no supera el DOI=3, 
										 *no es una instancia relevante y no se toma en cuanta*/
										if(instdoi>=3){
											/*ademas compruebo que el DOI sea mayor o igual al de la instancia anterior en la secuencia, 
											 *de otra manera la informacion que aporta es irrelevante*/
											if(instdoi>=getDOI(individual, user)){
												//creo una nueva secuencia con base en la secuencia original
												PropertySequence nuevaSecuencia = new PropertySequence(sequence.getSequence());
												nuevaSecuencia.add(instancia); //agrego la nueva instancia encontrada
												finalSequence.add(nuevaSecuencia); //agrego la nueva secuencia en la lista final
												existenNuevasSecuencias = true; //levanto la bandera de que una nueva secuencia fue encontrada
											}
										}
									}
								}
							}
						}
					}
				}				
			}
			//cuando termino la busqueda de nuevas secuencias relevantes compruebo que efectivamente se hallan encontrado nuevas secuencias
			if(!existenNuevasSecuencias){
				//si no se encontraron nuevas secuencias, la secuencia actual es una secuencia final
				finalSequence.add(sequence);
			}			
		}

		//Cuando termina la busqueda a partir de las secuencias anteriores, se comprueba si la la lista de secuencias in 
		//es diferente a la lista de secuencias luego de la busqueda, si son diferentes es porque se encontaron nuevas secuancias
		if(!in.equals(finalSequence)){
			System.out.print("|");
			//si se encontraron nuevas secuencias se continua la busqueda
			finalSequence = continueProperySequences(finalSequence, user,props);
		}
		//de otra manera, termino la busqueda
		return finalSequence;
	}
        

}
